home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 January: Mac OS SDK / Dev.CD Jan 00 SDK1.toast / What's New / • What was new 11⁄99 / Sample Code / Graphics 3D / OpenGL DrawSprocket / OpenGL DrawSprocket.c next >
Encoding:
C/C++ Source or Header  |  1999-09-20  |  17.7 KB  |  546 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OpenGL DrawSprocket.cp
  3.  
  4.     Contains:    An example of OpenGL and DrawSprocket integration for full screen drawing.  
  5.                 Handles multiple displays and timing of swaps per second.
  6.                 
  7.                 The optimal way to select a rendering context would be to check the available devices, find the best,
  8.                 then present the user with a selction dialog only if there are more than one "best" device.
  9.                 Currently there is not a good mechanism to do this easily, so the two best other options are
  10.                 
  11.                 1) iterate throught the available devices find the best renderer, build a DSp context on this,
  12.                 then build your OPenGL context.  The downfall of this method is that you know nothing about the graphics
  13.                 capabilities of the device and the user can't select another screen.  The upside is that you get 
  14.                 a renderer that is has the capabilities you want.
  15.                 
  16.                 2) Allow the user to select a screen to use, build a DSp context and then a OpenGL context.  This
  17.                 second method is simpler but relies on user knowing which device is the best at 3D.
  18.                 
  19.                 This sample illustrates the second method which makes it simpler and allows us to illustrate the
  20.                 DSp/OpenGL interface well.                
  21.  
  22.     Written by:    Geoff Stahl
  23.                 original code by John Stauffer
  24.  
  25.     Copyright:    1999 Apple Computer, Inc., All Rights Reserved
  26.  
  27.     Change History (most recent first):
  28.  
  29.          <6>     9/14/99    GGS        Corrected buffer rect handling and cleaned up code
  30.          <5>     7/14/99    GGS        Fixed multi-monitor window centering
  31.          <4>     7/13/99    GGS        Add work around for over zealous checking in single buffer DSp context attributes
  32.          <3>     7/5/99     GGS        Now correctly handle multi-monitor (DSp front buffer for single device; Window on top of context for multiple devices)
  33.          <2>     5/28/99    GGS     Added better multi-monitor support, clean code, corrected blanking bug, added timing, correct pixel formats
  34.          <1>        ?        ?      Initial build
  35.  
  36.     This code can be built for DrawSprocket 1.7.  Enable the #define kDSp17 below to add this.
  37.  
  38.       You may incorporate this sample code into your applications without
  39.       restriction, though the sample code has been provided "AS IS" and the
  40.       responsibility for its operation is 100% yours.  However, what you are
  41.       not permitted to do is to redistribute the source as "DSC Sample Code"
  42.       after having made changes. If you're going to re-distribute the source,
  43.      we require that you make it clear in the source that the code was
  44.     descended from Apple Sample Code, but that you've made changes.
  45.     
  46. */
  47.  
  48. // Building with DrawSprocket 1.7
  49. //#define kDSp17 1
  50.  
  51. #include <Fonts.h>
  52. #include <sound.h>
  53. #include <resources.h>
  54. #include <Folders.h>
  55. #include <Files.h>
  56. #include <textutils.h>
  57.  
  58. #include <DrawSprocket.h>
  59.  
  60. #include <math.h>
  61. #include <stdio.h>
  62. #include <string.h>
  63.  
  64. #include "gl.h"
  65. #include "aglRenderers.h"
  66. #include "agl.h"
  67.  
  68. //-----------------------------------------------------------------------------------------------------------------------
  69.  
  70. enum 
  71. {
  72.     kBitsPerPixel = 32,
  73.     kContextWidth = 400,
  74.     kContextHeight = 400
  75. };
  76.  
  77. //-----------------------------------------------------------------------------------------------------------------------
  78.  
  79. // globals
  80. NumVersion gVersionDSp;
  81. DSpContextAttributes gTheContextAttributes;
  82. DSpContextReference gTheContext;
  83. GDHandle ghGD;
  84.  
  85. const RGBColor    rgbBlack    = { 0x0000, 0x0000, 0x0000 };
  86. const RGBColor    rgbWhite    = { 0xFFFF, 0xFFFF, 0xFFFF };
  87.  
  88. //-----------------------------------------------------------------------------------------------------------------------
  89.  
  90. // function prototypes
  91. void CToPStr (register const char *inString, StringPtr outString );
  92. CGrafPtr SetupScreen (GDHandle *hGD, short *numDevices);
  93. void ShutdownScreen(CGrafPtr *ppWin);
  94. void aglDebugStr (void);
  95. AGLContext setupAGL(GDHandle hGD, AGLDrawable win, short numDevices);
  96. void cleanupAGL(AGLContext ctx);
  97. void drawGL(AGLContext ctx);
  98.  
  99. //-----------------------------------------------------------------------------------------------------------------------
  100.  
  101. // main: setup, draw loop, frames per swap timing, clean up (exit with mouse click)
  102.  
  103. int main(void)
  104. {
  105.     CGrafPtr pWin;
  106.     GDHandle hGD;
  107.     short numDevices = 0;
  108.     AGLContext ctx;
  109.     CGrafPtr portSave;
  110.     GDHandle hGDSave;
  111.     RGBColor colorSave;
  112.  
  113.     // timing info
  114.     long frames = 0, duration;
  115.     float fps;
  116.     short fNum, len;
  117.     Str255 aStr = "\p";
  118.     char aChar[256];
  119.     
  120.     // Mac Init
  121.     MaxApplZone ();
  122.     InitGraf(&qd.thePort);
  123.     InitFonts();
  124.     InitWindows();
  125.     InitMenus();
  126.     TEInit();
  127.     InitDialogs(nil);
  128.     InitCursor();
  129.             
  130.     pWin = SetupScreen(&hGD, &numDevices);                        // Setup DSp for OpenGL
  131.     if(!pWin) 
  132.         return 0;
  133.     ctx = setupAGL(hGD, (AGLDrawable) pWin, numDevices);        // Setup the OpenGL context
  134.     if(!ctx) 
  135.         return 0;    
  136.     
  137.     duration = TickCount ();                                    // timing start
  138.  
  139.     do {        
  140.         drawGL(ctx);                                            // OGL draw
  141.         frames++;
  142.     } while (!Button());
  143.     glFinish ();                                                // flush and sync
  144.  
  145.     // do frame per swap timing 
  146.     duration = TickCount () - duration; 
  147.     fps = (float) frames * 60.0 / duration;
  148.     sprintf (aChar, "Swaps/Sec: %0.1f", fps);
  149.     CToPStr (aChar, aStr);
  150.     // set up for screen output 
  151.     GetGWorld (&portSave, &hGDSave);
  152.     SetGWorld (pWin, hGD);
  153.     GetForeColor (&colorSave);
  154.     RGBForeColor (&rgbWhite);
  155.     // set text
  156.     GetFNum("\pGeneva", &fNum);
  157.     TextFont(fNum);
  158.     TextSize(12);
  159.     // draw centered to screen
  160.     len = StringWidth (aStr);
  161.      MoveTo (pWin->portRect.left + (pWin->portRect.right - pWin->portRect.left) / 2 - (len / 2), 18);
  162.     DrawString(aStr);
  163.     // wait for click to continue
  164.     while (Button()) {};
  165.     while (!Button()) {};
  166.     // reset colors
  167.     RGBForeColor (&colorSave);
  168.     SetGWorld (portSave, hGDSave);
  169.  
  170.     cleanupAGL(ctx);                                            // Cleanup the OpenGL context
  171.     ShutdownScreen(&pWin);                                        // DSp shutdown
  172.     
  173.     FlushEvents(everyEvent, 0);
  174.     return 0;
  175. }
  176.  
  177. //-----------------------------------------------------------------------------------------------------------------------
  178.  
  179. // Copy C string to Pascal string
  180.  
  181. void CToPStr (register const char *inString, StringPtr outString ) // copies in to out
  182. {    
  183.     register unsigned char x = 0;
  184.     do
  185.     {
  186.         *(((char*)outString) + 1 + x) = *(inString + x);
  187.         x++;
  188.     }
  189.     while ((*(inString + x) != 0)  && (x < 256));
  190.     *((char*)outString) = (char) x;                                    
  191. }
  192.  
  193. //-----------------------------------------------------------------------------------------------------------------------
  194.  
  195. // Set up DSp screens, handles multi-monitor correctly
  196.  
  197. CGrafPtr SetupScreen (GDHandle *phGD, short *numDevices)
  198. {
  199.     GDHandle hDevice;
  200.     CGrafPtr pCGOut;
  201.     DSpContextAttributes foundAttributes;
  202.     DisplayIDType displayID;
  203.     Rect rectWin;
  204.     RGBColor rgbSave;
  205.     GrafPtr pGrafSave;
  206.     *numDevices = 0;
  207.  
  208.     // check for DSp
  209.     if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpStartup) 
  210.     {
  211.         DebugStr("\pDSp not installed\n");
  212.         phGD = NULL;
  213.         numDevices = 0;
  214.         return NULL;
  215.     }    
  216.  
  217. #ifdef kDSp17
  218.     if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpGetVersion) 
  219. #endif
  220.     {
  221.         // must be 1.1.4 or earlier so look at extension
  222.         short resFileSave, resFileDSp;
  223.         short vRefDSp;
  224.         long dirIDDSp;
  225.         FSSpec fsSpecDSp;
  226.         OSErr err;
  227.         Handle hVersion;
  228.         
  229.         // assume 1.0 version to start with (worst case)
  230.         gVersionDSp.majorRev = 1;
  231.         gVersionDSp.minorAndBugRev = 0;
  232.         gVersionDSp.stage = 0x80;
  233.         gVersionDSp.nonRelRev = 0;
  234.  
  235.         resFileSave = CurResFile();
  236.         SetResLoad (false);
  237.         // search application directory for debug lib
  238.         resFileDSp = OpenResFile ("\pDrawSprocketDebugLib");
  239.         err = ResError ();
  240.         if (fnfErr == err)
  241.         {
  242.             // search application directory for lib
  243.             resFileDSp = OpenResFile ("\pDrawSprocketLib");
  244.             err = ResError ();
  245.             if (fnfErr == err)
  246.             {
  247.                 // search extensions folder for debug lib
  248.                 FindFolder (kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &vRefDSp, &dirIDDSp);
  249.                 FSMakeFSSpec (vRefDSp, dirIDDSp, "\pDrawSprocketLib", &fsSpecDSp);
  250.                 resFileDSp = FSpOpenResFile (&fsSpecDSp, fsRdPerm);
  251.                 err = ResError ();
  252.                 if (fnfErr == err)
  253.                 {
  254.                     // search extensions folder directory for lib
  255.                     FSMakeFSSpec (vRefDSp, dirIDDSp, "\pDrawSprocketDebugLib", &fsSpecDSp);
  256.                     resFileDSp = FSpOpenResFile (&fsSpecDSp, fsRdPerm);
  257.                     err = ResError ();
  258.                 }
  259.             }
  260.         }
  261.         // if we have an open resource file and no error
  262.         if ((noErr == err) && (-1 != resFileDSp))
  263.         {
  264.             SetResLoad (true);
  265.             hVersion = GetResource ('vers', 1); 
  266.             err = ResError ();
  267.             if ((noErr == err) && (NULL != hVersion))
  268.             {
  269.                 gVersionDSp.majorRev = *(((unsigned char *)*hVersion) + 0);
  270.                 gVersionDSp.minorAndBugRev = *(((unsigned char *)*hVersion) + 1);
  271.                 gVersionDSp.stage = *(((unsigned char *)*hVersion) + 2);
  272.                 gVersionDSp.nonRelRev = *(((unsigned char *)*hVersion) + 3);
  273.                 ReleaseResource (hVersion);
  274.             }
  275.             UseResFile(resFileSave);
  276.             CloseResFile(resFileDSp);
  277.         }    
  278.     }
  279. #ifdef kDSp17
  280.     else
  281.         gVersionDSp = DSpGetVersion ();
  282. #endif
  283.  
  284.     hDevice = DMGetFirstScreenDevice (true);                                // check number of screens
  285.     do
  286.     {
  287.         (*numDevices)++;
  288.         hDevice = DMGetNextScreenDevice (hDevice, true);
  289.     }
  290.     while (hDevice);
  291.     
  292.     if(noErr != DSpStartup())                                                         // start DSp and find a good context
  293.         DebugStr("\pUnable to startup\n");
  294.         
  295.     // Note: DSp currently REQUIRES the back buffer attributes even if only one buffer is required
  296.     memset(&gTheContextAttributes, 0, sizeof (DSpContextAttributes));
  297.     gTheContextAttributes.displayWidth            = kContextWidth;
  298.     gTheContextAttributes.displayHeight            = kContextHeight;
  299.     gTheContextAttributes.colorNeeds            = kDSpColorNeeds_Require;
  300.     gTheContextAttributes.displayBestDepth        = kBitsPerPixel;
  301.     gTheContextAttributes.backBufferBestDepth    = kBitsPerPixel;
  302.     gTheContextAttributes.displayDepthMask        = kDSpDepthMask_All;
  303.     gTheContextAttributes.backBufferDepthMask    = kDSpDepthMask_All;
  304.     gTheContextAttributes.pageCount                = 1;                                // only the front buffer is needed
  305.     
  306.     // will display user dialog if context selection is required otherwise as find best context
  307.     if (noErr != DSpUserSelectContext(&gTheContextAttributes, 0L, nil, &gTheContext))
  308.         DebugStr("\pA suitable display context could not be found.");
  309.     if (noErr != DSpContext_GetAttributes (gTheContext, &foundAttributes))             // see what we actually found
  310.         DebugStr("\pUnable to find a suitable device\n");
  311.         
  312.     // reset width and height to full screen and handle our own centering
  313.     // HWA will not correctly center less than full screen size contexts
  314.     gTheContextAttributes.displayWidth         = foundAttributes.displayWidth;
  315.     gTheContextAttributes.displayHeight     = foundAttributes.displayHeight;
  316.     gTheContextAttributes.pageCount            = 1;                                    // only the front buffer is needed
  317.     gTheContextAttributes.contextOptions    = 0 | kDSpContextOption_DontSyncVBL;    // no page flipping and no VBL sync needed
  318.  
  319.     DSpSetBlankingColor(&rgbBlack);
  320.     if (noErr != DSpContext_GetDisplayID(gTheContext, &displayID))                     // get our device for future use
  321.         DebugStr("\pDSpContext_GetDisplayID() had an error.");
  322.     if (noErr != DMGetGDeviceByDisplayID(displayID, phGD, false))                     // get GDHandle for ID'd device
  323.         DebugStr("\pDMGetGDeviceByDisplayID() had an error.");
  324.     if (noErr != DSpContext_Reserve( gTheContext, &gTheContextAttributes ))         // reserve our context
  325.         DebugStr("\pUnable to create the display!");
  326.  
  327.     HideCursor ();
  328.  
  329.     if (noErr != DSpContext_FadeGammaOut (NULL, NULL))                                 // fade display, remove for debug
  330.         DebugStr("\pUnable to fade the display!");
  331.     if (noErr != DSpContext_SetState (gTheContext, kDSpContextState_Active))        // activate our context
  332.         DebugStr("\pUnable to set the display!");
  333.     
  334.     // create a new window in our context (required for multi-monitor handling) 
  335.     //     for single monitor we just use the DSp context
  336.     // note: OpenGL is expecting a window so it can enumerate the devices it spans, 
  337.     //         if you us a CGrafPtr instead it MUST be on the main device and always remain there
  338.     if (*numDevices > 1)    // multi-monitor
  339.     {
  340.         // center window in our context's gdevice
  341.         rectWin.top  = (***phGD).gdRect.top + ((***phGD).gdRect.bottom - (***phGD).gdRect.top) / 2;          // h center
  342.         rectWin.top  -= kContextHeight / 2;
  343.         rectWin.left  = (***phGD).gdRect.left + ((***phGD).gdRect.right - (***phGD).gdRect.left) / 2;    // v center
  344.         rectWin.left  -= kContextWidth / 2;
  345.         rectWin.right = rectWin.left + kContextWidth;
  346.         rectWin.bottom = rectWin.top + kContextHeight;
  347.         
  348.         pCGOut = (CGrafPtr)NewCWindow (NULL, &rectWin, "\p", 0, plainDBox, (WindowPtr)-1, 0, 0);
  349.     
  350.         // paint back ground black before fade in to avoid white background flash
  351.         ShowWindow((GrafPtr)pCGOut);
  352.         GetPort (&pGrafSave);
  353.         SetPort ((GrafPtr)pCGOut);
  354.         GetForeColor (&rgbSave);
  355.         RGBForeColor (&rgbBlack);
  356.         PaintRect (&pCGOut->portRect);
  357.         RGBForeColor (&rgbSave);        // ensure color is reset for proper blitting
  358.         SetPort (pGrafSave);
  359.     }
  360.     else // single screen (just use front buffer, but must set glviewport and buffer rect)
  361.         if (noErr != DSpContext_GetFrontBuffer( gTheContext, &pCGOut)) // get our buffer to draw into
  362.             DebugStr("\pUnable to get front buffer");
  363.     
  364.     if (noErr != DSpContext_FadeGammaIn (NULL, NULL))
  365.         DebugStr("\pUnable to fade the display!");    
  366.     
  367.     return pCGOut;
  368. }
  369.  
  370. //-----------------------------------------------------------------------------------------------------------------------
  371.  
  372. // clean up DSp
  373.  
  374. void ShutdownScreen (CGrafPtr *ppWin)
  375. {
  376.     DSpContext_FadeGammaOut (NULL, NULL);
  377.     if (*ppWin)
  378.         DisposeWindow ((WindowPtr)*ppWin);
  379.     *ppWin = NULL;
  380.     DSpContext_SetState( gTheContext, kDSpContextState_Inactive );
  381.     DSpContext_FadeGammaIn (NULL, NULL);
  382.     ShowCursor ();
  383.     DSpContext_Release (gTheContext);
  384.     DSpShutdown ();
  385. }
  386.  
  387. //-----------------------------------------------------------------------------------------------------------------------
  388.  
  389. // Dump agl errors to debugger string
  390.  
  391. void aglDebugStr (void)
  392. {
  393.     Str255 errStr;
  394.     unsigned char * inErrStr = (unsigned char *) aglErrorString(aglGetError());
  395.     short x = 0;
  396.     do
  397.         errStr [++x] = *inErrStr;
  398.     while (*(inErrStr++) && (x < 256));
  399.     errStr [0] = x - 1;
  400.     DebugStr (errStr);
  401. }
  402.  
  403. //-----------------------------------------------------------------------------------------------------------------------
  404.  
  405. // OpenGL Setup
  406.  
  407. AGLContext setupAGL (GDHandle hGD, AGLDrawable win, short numDevices)
  408. {
  409. // different possible pixel format choices for different renderers 
  410. // (note: AGL_NO_RECOVERY is added to prevent failure on multi-monitor systems)
  411. // basics requirements are RGBA and double buffer
  412. // OpenGL will select acclerated context if available
  413.  
  414. // software renderer
  415. //    GLint          attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_RENDERER_ID, AGL_RENDERER_GENERIC_ID, AGL_ALL_RENDERERS, AGL_DOUBLEBUFFER, AGL_NONE };
  416.  
  417. // any renderer
  418.     GLint          attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_ALL_RENDERERS, AGL_DOUBLEBUFFER, AGL_NONE };
  419.  
  420. // OpenGL compliant HWA renderer 
  421. //    GLint          attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_ACCELERATED, AGL_DOUBLEBUFFER, AGL_NONE };
  422.  
  423. // OpenGL compliant ATI renderer 
  424. //    GLint          attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_RENDERER_ID, AGL_RENDERER_ATI_ID, AGL_DOUBLEBUFFER, AGL_NONE };
  425.  
  426. // OpenGL compliant ATI HWA renderer 
  427. //    GLint          attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_RENDERER_ID, AGL_RENDERER_ATI_ID, AGL_ACCELERATED, AGL_DOUBLEBUFFER, AGL_NONE };
  428.  
  429.     GLint          bufferPos [4];                                // x, y (inverted), w (clip), h (clip)
  430.     AGLPixelFormat fmt;
  431.     AGLContext     ctx;
  432.  
  433.     if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat) // check for existance of OpenGL
  434.     {
  435.         DebugStr("\pOpenGL not installed\n");
  436.         return NULL;
  437.     }    
  438.  
  439.     if (hGD)                                                    // Choose an rgb pixel format use a device if we have one
  440.     {
  441.         fmt = aglChoosePixelFormat(&hGD, 1, attrib);            // get an appropriate pixel format
  442.         if (NULL == fmt) 
  443.         {
  444.             aglDebugStr ();
  445.             return NULL;
  446.         }
  447.     }
  448.     else
  449.     {
  450.         fmt = aglChoosePixelFormat(NULL, 0, attrib);            // get an appropriate pixel format
  451.         if (NULL == fmt) 
  452.         {
  453.             aglDebugStr ();
  454.             return NULL;
  455.         }
  456.     }
  457.  
  458.     ctx = aglCreateContext (fmt, NULL);                            // Create an AGL context
  459.     if(NULL == ctx) 
  460.     {
  461.         aglDebugStr ();
  462.         return NULL;
  463.     }
  464.     
  465.     if (1 == numDevices)    // ensure that the buffer rect is set to limit OpenGL memory requirements
  466.     {                        // when setting the drawable
  467.         // set buffer rectangle
  468.         bufferPos [0]  = win->portRect.left + (win->portRect.right - win->portRect.left) / 2;    // h center
  469.         bufferPos [0]  -= kContextWidth / 2;
  470.         bufferPos [1]  = win->portRect.top + (win->portRect.bottom - win->portRect.top) / 2;    // v center
  471.         bufferPos [1]  -= kContextHeight / 2;
  472.         bufferPos [2]  = kContextWidth ;
  473.         bufferPos [3]  = kContextHeight;
  474.         if (!aglSetInteger (ctx, AGL_BUFFER_RECT, bufferPos))    // turn on buffer rect
  475.         {
  476.             aglDebugStr ();
  477.             return NULL;
  478.         }
  479.         if(!aglEnable (ctx, AGL_BUFFER_RECT))                    // enable buffer rectangle 
  480.         {
  481.             aglDebugStr ();
  482.             return NULL;
  483.         }
  484.     }
  485.  
  486.     if(!aglSetDrawable(ctx, win))                                // attach the CGrafPtr to the context
  487.     {
  488.         aglDebugStr ();
  489.         return NULL;
  490.     }
  491.     
  492.     if(!aglSetCurrentContext(ctx))                                // make the context the current context
  493.     {
  494.         aglDebugStr ();
  495.         return NULL;
  496.     }
  497.  
  498.     if (numDevices == 1)
  499.         glViewport (0, 0, kContextWidth, kContextHeight);        // scale viewport to buffer rectangle size
  500.  
  501.     aglDestroyPixelFormat(fmt);                                    // pixel format is no longer needed
  502.     
  503.     return ctx;
  504. }
  505.  
  506. //-----------------------------------------------------------------------------------------------------------------------
  507.  
  508. // OpenGL Cleanup
  509.  
  510. void cleanupAGL(AGLContext ctx)
  511. {
  512.     aglSetCurrentContext(NULL);
  513.     aglSetDrawable(ctx, NULL);
  514.     aglDestroyContext(ctx);
  515. }
  516.  
  517. //-----------------------------------------------------------------------------------------------------------------------
  518.  
  519. // OpenGL Drawing
  520.  
  521. void drawGL(AGLContext ctx)
  522. {
  523.     static float f, s, c;
  524.     GLboolean fState = GL_FALSE;
  525.  
  526.     f += 0.01;
  527.     s = sin(f);
  528.     c = cos(f);
  529.  
  530.     glClearColor(0.15f, 0.15f, 0.15f, 1.0f);                    // Clear color buffer to dark grey
  531.     glClear(GL_COLOR_BUFFER_BIT);
  532.     
  533.     glBegin(GL_POLYGON);                                        // Draw a smooth shaded polygon
  534.     glColor3d(1.0, 0.0, 0.0);
  535.     glVertex3d(s, c, 0.0);
  536.     glColor3d(0.0, 1.0, 0.0);
  537.     glVertex3d(-c, s, 0.0);
  538.     glColor3d(0.0, 0.0, 1.0);
  539.     glVertex3d(-s, -c, 0.0);
  540.     glColor3d(0.7, 0.7, 0.7);
  541.     glVertex3d(c, -s, 0.0);
  542.     glEnd();
  543.  
  544.     aglSwapBuffers(ctx);                                        // send swap command
  545. }
  546.